package com.example.Windturbine.QA;

import com.example.Windturbine.Dao.ChickenDOMapper;
import com.example.Windturbine.Dao.QADOMapper;
import com.example.Windturbine.Dao.UserActionDOMapper;
import com.example.Windturbine.DataObject.ChickenDO;
import com.example.Windturbine.DataObject.QADO;
import com.example.Windturbine.DataObject.UserActionDO;
import com.example.Windturbine.QA.QAObject.HandLPTree;
import org.apache.http.NameValuePair;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.util.EntityUtils;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;
import java.io.IOException;
import java.util.*;


/**
 * @Author 丁永新
 * @Date 2022/2/5
 */

public class QARobot {


    QADOMapper qadoMapper;

    UserActionDOMapper UserActionDOMapper;

    RedisTemplate redisTemplate;

    Integer userId;

    ChickenDOMapper chickenDOMapper;

    public QARobot(ChickenDOMapper chickenDOMapper,QADOMapper qadoMapper, com.example.Windturbine.Dao.UserActionDOMapper userActionDOMapper, RedisTemplate redisTemplate,Integer userId) {
        this.qadoMapper = qadoMapper;
        UserActionDOMapper = userActionDOMapper;
        this.redisTemplate = redisTemplate;
        this.userId=userId;
        this.chickenDOMapper=chickenDOMapper;
    }

    public QARobot() {

    }

    public String query(String question) {
        String origin=question;
        //1 过滤非法字符
        question=handle(question);
        //2 清空或加载缓存的命令
        if(question.equals("begin")){
            getChickenQAs();
            redisTemplate.delete("qas");
            getAllQAs();
            return "清空qas、重新加载qas、第一次加载chicken \n 加载完毕！";
        }

        if(question.equals("clear")){
            redisTemplate.delete("qas");
            Set<String> keys=redisTemplate.keys("cal"+"*");
            redisTemplate.delete(keys);
            getAllQAs();
            return "语料和计算的redis缓存已清空！";
        }

        //3 操作和问答相关语料比较
        String result = "对不起，小壤听不懂你说的话，请尝试用更简洁的问法。";
        HashMap<Integer, HandLPTree> questions=getAllQAs();
        HandLPTree current=toTree(question);
        PriorityQueue<double[]> queue = new PriorityQueue<double[]>((a,b)->{
            return (int) ((b[1]-a[1])*1000);
        });
        for(Map.Entry<Integer,HandLPTree> map:questions.entrySet()){
            double[] temp=new double[2];
            Integer index=Integer.parseInt(String.valueOf(map.getKey()));
            temp[0]=index+0.0;
            temp[1]= calsimilarity(current,map.getValue());
            if(temp[1]>=0.97){
                queue.offer(temp);
                break;
            }
            queue.offer(temp);
        }
        if(!queue.isEmpty()&&queue.peek()[1]>=0.6){
            UserActionDO t=new UserActionDO();
            t.setQuery(origin);
            t.setQuestionId((int)queue.peek()[0]);
            t.setUsreId(userId);
            UserActionDOMapper.insertSelective(t);
            return qadoMapper.selectByPrimaryKey((int)queue.peek()[0]).getAnswer();
        }

        //4 小黄鸡闲聊语料测试
        HandLPTree cur=toTree(origin);
        HashMap<Integer, HandLPTree> chickens=getAllQAs();
        for(Map.Entry<Integer,HandLPTree> map:chickens.entrySet()){
            double[] temp=new double[2];
            Integer index=Integer.parseInt(String.valueOf(map.getKey()));
            temp[0]=index+0.0;
            temp[1]= calsimilarity(cur,map.getValue());
            if(temp[1]>=0.95){
                UserActionDO t=new UserActionDO();
                t.setQuery(origin);
                t.setQuestionId(-1);
                t.setUsreId(userId);
                UserActionDOMapper.insertSelective(t);
                return chickenDOMapper.selectByPrimaryKey((int)temp[0]).getAnswer();
            }
        }

        //5 用户提问的问题入库保存
        UserActionDO t=new UserActionDO();
        t.setQuery(origin);
        t.setQuestionId(-1);
        t.setUsreId(userId);
        UserActionDOMapper.insertSelective(t);
        return result+"\n 您是否想问："+qadoMapper.selectByPrimaryKey((int)queue.peek()[0]).getQuestion()+"\n 如果是请输入该句子。";
    }


    public String handle(String question){
        question=question.replaceAll("[\\pP|~|$|^|<|>|\\||\\+|=]*", "");
        question=QtoB(question);
        question=question.toLowerCase();
        question=question.replaceAll("小壤", "");
        question=question.replaceAll("助手", "");
        question=question.replaceAll("小助手", "");
        question=question.replaceAll("，", "");
        question=question.replaceAll(",", "");
        question=question.replaceAll("\\?", "");
        question=question.replaceAll("？", "");
        question=question.replaceAll("你能告诉我", "");
        question=question.replaceAll("你可以告诉我", "");
        question=question.replaceAll("请你告诉我", "");
        question=question.replaceAll("请告诉我", "");
        question=question.replaceAll("告诉我", "");
        question=question.replaceAll("tell me", "");
        question=question.replaceAll("吗", "");
        question=question.replaceAll("我想知道", "");
        question=question.replaceAll("请问", "");
        question=question.replaceAll("如何理解", "什么是");
        question=question.replaceAll("can you tell me", "");
        question=question.replaceAll("i want to konw", "");
        question=question.replaceAll("啥", "什么");
        question=question.replaceAll("is", "是");
        question=question.replaceAll("what", "什么");
        question=question.replaceAll("怎样", "怎么");
        question=question.replaceAll("how", "怎样");
        return question;
    }

    public  String QtoB(String input) {
        char c[] = input.toCharArray();
        for (int i = 0; i < c.length; i++) {
            if (c[i] == '\u3000') {c[i] = ' ';
            }
            else if (c[i] > '\uFF00' && c[i] < '\uFF5F') {
                c[i] = (char) (c[i] - 65248);   //全角与半角之间相差 65248，相减即可
            }
        }
        return new String(c);
    }



    public HashMap<Integer, HandLPTree> getAllQAs() {
        if (redisTemplate.opsForValue().get("qas") == null) {
            List<QADO> qados = qadoMapper.queryAll();
            HashMap<Integer, HandLPTree> trees = new HashMap<Integer, HandLPTree>();
            for (QADO i : qados) {
                String q=i.getQuestion();
                System.out.println("qa"+q);
                q=q.replaceAll("[\\pP|~|$|^|<|>|\\||\\+|=]*", "");
                q=QtoB(q);
                q=q.toLowerCase();
                Integer id=i.getId();
                if(q.length()==1) {
                    HandLPTree t=new HandLPTree();
                    t.setWord(q);
                    trees.put(id,t);
                }else{
                    HandLPTree t=toTree(q);
                    trees.put(id,t);
                }

            }
            redisTemplate.opsForValue().set("qas", trees);
        }
        return (HashMap<Integer, HandLPTree>) redisTemplate.opsForValue().get("qas");
    }


    public HashMap<Integer, HandLPTree> getChickenQAs() {

/*        if (redisTemplate.opsForValue().get("chicken") == null) {
            List<ChickenDO> qados = chickenDOMapper.queryAll();
            HashMap<Integer, HandLPTree> trees = new HashMap<Integer, HandLPTree>();
            for (ChickenDO i : qados) {
                String q=i.getQuestion();
                System.out.println("chicken"+q);
                q=q.replaceAll("[\\pP|~|$|^|<|>|\\||\\+|=]*", "");
                q=QtoB(q);
                q=q.toLowerCase();
                Integer id=i.getId();
                HandLPTree t=toTree(q);
                trees.put(id,t);
            }
            redisTemplate.opsForValue().set("chicken", trees);
        }
        return (HashMap<Integer, HandLPTree>) redisTemplate.opsForValue().get("chicken");*/
        redisTemplate.opsForValue().set("chicken", new HashMap<Integer, HandLPTree>());
        return (HashMap<Integer, HandLPTree>) redisTemplate.opsForValue().get("chicken");
    }



    public double calsimilarity(HandLPTree tree1, HandLPTree tree2) {
        HashMap<String,Integer> temp=new HashMap<String,Integer>();
        HashMap<String, Integer> wordes1 = getWordes(tree1);
        HashMap<String, Integer> wordes2= getWordes(tree2);
        List<String> sameWords = findSameWords(wordes1, wordes2);
        if(sameWords.size()==0) return 0.0;
        return 1.0*(calInfoEntropy(tree1,sameWords,wordes1.size()) + calInfoEntropy(tree2,sameWords, wordes2.size()))/2;
    }

    public double calInfoEntropy(HandLPTree root,List<String> sameWords,int len){
        HashSet<String> set=new HashSet<String>();
        for(String s:sameWords) set.add(s);
        double totalEntropy=0.0;
        double sameEntropy=0.0;
        if(root==null) return 0.0;
        Queue<HandLPTree> queue=new LinkedList<HandLPTree>();
        queue.offer(root);
        while(!queue.isEmpty()){
            int size=queue.size();
            totalEntropy=totalEntropy*1.3;
            sameEntropy=sameEntropy*1.3;
            for(int i=0;i<size;i++){
                HandLPTree t=queue.poll();
                totalEntropy=totalEntropy-(1.0/len)*Math.log((1.0/2*len))*1.3;
                if(set.contains(t.getWord())){
                    sameEntropy=sameEntropy-(1.0/len)*Math.log((1.0/2*len))*1.3;
                }
                HashMap<Integer, HandLPTree> tSubtree= t.getSubTrees();
                for(Map.Entry<Integer,HandLPTree> map:tSubtree.entrySet()){
                    queue.offer(map.getValue());
                }
            }
        }
        if(totalEntropy==0.0) return 0.0;
        return sameEntropy/totalEntropy;
    }


    public List<String> findSameWords(HashMap<String, Integer> wordes1,HashMap<String, Integer> wordes2){
        if(wordes1.size() > wordes2.size()){
            return findSameWords(wordes2,wordes1);
        }
        ArrayList<String> res=new ArrayList<>();
        for(Map.Entry<String,Integer> map:wordes1.entrySet()){
            if(wordes2.containsKey(map.getKey())){
                int n=Math.min(wordes1.get(map.getKey()),wordes2.get(map.getKey()));
                while(n!=0){
                    res.add(map.getKey());
                    n--;
                }
            }
        }
        return res;
    }

    public HashMap<String,Integer> getWordes(HandLPTree root){
        HashMap<String,Integer> res=new HashMap<String,Integer>();
        if(root==null) return res;
        Queue<HandLPTree> queue=new LinkedList<HandLPTree>();
        queue.offer(root);
        while(!queue.isEmpty()){
            int size=queue.size();
            for(int i=0;i<size;i++){
                HandLPTree t=queue.poll();
                if(res.containsKey(t.getWord())){
                    res.put(t.getWord(),res.get(t.getWord()) +1);
                }else{
                    res.put(t.getWord(),1);
                }
                HashMap<Integer, HandLPTree> tSubtree= t.getSubTrees();
                if(tSubtree!=null&&tSubtree.size()!=0){
                    for(Map.Entry<Integer,HandLPTree> map:tSubtree.entrySet()){
                        queue.offer(map.getValue());
                    }
                }

            }
        }
        return res;
    }




    /**
     * 将结果转化为有一棵树
     *
     * @param str
     * @return
     */
    public  HandLPTree toTree(String str) {
        HandLPTree tree = new HandLPTree();
        if (str.length()==1){
            tree.setWord(str);
            return tree;
        }
        String dependence = dependence(str);
        dependence = dependence.replaceAll("\\[\\[\\[", "stop");
        dependence = dependence.replaceAll("\\]\\]\\]", "stop");
        dependence = dependence.replaceAll("\\[\\[", "stop");
        dependence = dependence.replaceAll("\\]\\]", "stop");
        dependence = dependence.replaceAll("\\],\\[", ",");
        String[] res = dependence.split("stop");
        String word = res[1];
        String dep = res[3];
        String[] temp1=word.split(",");
        String[] temp2=dep.split(",");

        ArrayList<String> words=new ArrayList<String>();
        ArrayList<Integer> deps=new  ArrayList<Integer>();

        for(String s:temp1){
            words.add(s.substring(1,s.length()-1));
        }
        for(String s:temp2){
            Character c=s.charAt(0);
            if(Character.isDigit(c)){
                deps.add(Integer.parseInt(s)-1);
            }
        }
        HashMap<Integer,HandLPTree> treemap=new HashMap<Integer,HandLPTree>();
        for(int i=0;i<words.size();i++){
            HandLPTree temp=new HandLPTree();
            temp.setIndex(i);
            temp.setWord(words.get(i));
            temp.setParent(deps.get(i));
            temp.setSubTrees(new HashMap<Integer, HandLPTree>());
            treemap.put(temp.getIndex(),temp);
        }
        tree=converFromMap(treemap);
        return tree;
    }


    public  HandLPTree converFromMap(HashMap<Integer,HandLPTree> treemap){
        HandLPTree root = null;
        //先找根节点
        for(Map.Entry<Integer,HandLPTree> map:treemap.entrySet()){
            if(map.getValue().getParent()==-1){
                root=map.getValue();
                break;
            }
        }
        int size=0;
        while(size<treemap.size()){
            for(Map.Entry<Integer,HandLPTree> map:treemap.entrySet()){
                root=addNode(root,map.getValue());
            }
            size++;
        }
        return root;
    }

    public  HandLPTree addNode(HandLPTree root,HandLPTree node){
        if(root==null) return root;
        Integer parent=node.getParent();
        Queue<HandLPTree> queue=new LinkedList<HandLPTree>();
        queue.offer(root);
        while(!queue.isEmpty()){
            int size=queue.size();
            for(int i=0;i<size;i++){
                HandLPTree t=queue.poll();
                if(t.getIndex()==parent&&!t.getSubTrees().containsKey(node.getIndex())){
                    t.getSubTrees().put(node.getIndex(),node);
                    return root;
                }
                HashMap<Integer, HandLPTree> tSubtree= t.getSubTrees();
                for(Map.Entry<Integer,HandLPTree> map:tSubtree.entrySet()){
                    queue.offer(map.getValue());
                }
            }
        }
        return root;
    }



    /**
     * 返回分析结果
     *
     * @param str
     * @return
     */
    public  String dependence(String str) {
        String token = "7db3ade46d0644379b9fd5c8b1634dbe1644059714562token";
        String url = "http://comdo.hanlp.com/hanlp/v21/dep/dep";
        Map<String, Object> params = new HashMap<String, Object>();
        params.put("text", str);
        String result = doHanlpApi(token, url, params);
        return result;
    }


    /**
     * 发送请求进行文本分析
     *
     * @param token
     * @param url
     * @param params
     * @return
     */
    public  String doHanlpApi(String token, String url, Map<String, Object> params) {
        // 创建Httpclient对象
        CloseableHttpClient httpClient = HttpClients.createDefault();
        CloseableHttpResponse response = null;
        String resultString = "";
        try {
            // 创建Http Post请求
            HttpPost httpPost = new HttpPost(url);
            //添加header请求头，token请放在header里
            httpPost.setHeader("token", token);
            // 创建参数列表
            List<NameValuePair> paramList = new ArrayList<>();
            if (params != null) {
                for (String key : params.keySet()) {
                    //所有参数依次放在paramList中
                    paramList.add(new BasicNameValuePair(key, (String) params.get(key)));
                }
                //模拟表单
                UrlEncodedFormEntity entity = new UrlEncodedFormEntity(paramList, "utf-8");
                httpPost.setEntity(entity);
            }
            // 执行http请求
            response = httpClient.execute(httpPost);
            resultString = EntityUtils.toString(response.getEntity(), "utf-8");
            return resultString;
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (response != null) {
                try {
                    response.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
        return null;
    }
}



